package cn.remex.core.util;

import cn.remex.core.exception.FunctionException;
import cn.remex.core.exception.NestedException;
import cn.remex.core.exception.ServiceCode;
import cn.remex.core.reflect.CommConstructor;
import cn.remex.core.reflect.CommGetter;
import cn.remex.core.reflect.CommSetter;
import cn.remex.core.reflect.ReflectFeatureStatus;
import cn.remex.core.reflect.ReflectUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static cn.remex.core.util.Judgment.nullOrBlank;
/**
 * Map处理工具类
 */

/**
 * @author liuhengyang
 *         date 2014-9-24 下午2:31:15
 * @version 版本号码
 */
public class MapHelper {

	private static Method instanceMehtod;

	static {
		try {
			instanceMehtod = ReflectUtil.class.getMethod("invokeNewInstance", Class.class);
		} catch (NoSuchMethodException e) {
			throw new NestedException(ServiceCode.ERROR, "MapHelper初始化时无法获取ReflectUtil的核心方法invokeNewInstance。", e);
		}
	}

	/**
	 * <br>将数据扁平化变成一个简单的二维map列表,如:
	 * <br>a-1
	 * <br>b-2
	 * <br>c-LHY
	 * <br>d.id-SN001
	 * <br>d.name-test
	 * <p>规则如下：
	 * 1.基本类型、String、Enum作为base数据处理。
	 * 2.Object,Map 当做统一的object数据处理，通过.来引用属性。 Map的key必须支持toString()
	 * 3.Collection 当做数组处理，通过[n]引用属性。
	 * 4.特殊类型通过toString()方法处理。
	 * <p>
	 * <br>
	 * <p>要点:
	 * 1.只能序列化对象在存在getter方法的属性
	 *
	 * @param o 对象
	 * @return map 返回一个扁平的Map对象
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, String> objectToFlat(Object o) {
		return (Map<String, String>) _toFlatData(o, "", null);
	}

	/*
	 * 根据对象的结构生成一个树形结构的maptree
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> objectToMapTree(Object o) {
		return (Map<String, Object>) _toMapTree(o);
	}

	/*
	 * 将key-value列表转化为一个对象。
	 * key可是是包含".""[]"的表达式。
	 */
	public static void objectFromFlat(Object t, Map<String, Object> map) {
		for (String key : map.keySet()) {
			Object value = map.get(key);
			evalFlatValueToObjectField(t, key, value, ReflectUtil.class, instanceMehtod);
		}
	}

	/*
	 * 将key-value列表转化为一个对象。
	 * key可是是包含".""[]"的表达式。
	 *                如果需要生成新的对象，采用默认的ReflectUtils.invokeNewInstance
	 */
	public static void evalFlatValueToObjectField(Object t, String flatKey, Object value) {
		evalFlatValueToObjectField(t, flatKey, value, ReflectUtil.class, instanceMehtod);
	}

	/*
	 * 根据flatkey设置目标对象的值。
	 */
	public static void evalFlatValueToObjectField(Object t, String flatKey, Object value, Object instanceFactory, Method instanceMethod) {
		try {
			_flat2Object(t, flatKey, value, instanceFactory, instanceMethod);
		} catch (InvocationTargetException e) {
			throw new NestedException(ServiceCode.ERROR, "MapHelper中进行链式表达式赋值时，无法构建实例属性。", e);
		} catch (IllegalAccessException e) {
			throw new NestedException(ServiceCode.ERROR, "MapHelper中进行链式表达式赋值时，无法构建实例属性。", e);
		}
	}


	/**
	 * 将对象的编制为Map，Map中的key为属性名称，value为属性对应的值。空值(null)将被忽略
	 *
	 * @param obj java对象
	 * @return {@link Map}
	 */
	public static Map<String, Object> toMap(Object obj) {
		return toMap(obj, value -> value);
	}
	public static Map<String, Object> toMap(Object obj, Function<Object,Object> valuePerHandle) {
		Assert.notNull(obj, ServiceCode.ERROR, "需要转化的对象不可以为空");

		Map<String, Object> map = new HashMap<String, Object>();

		Map<String, Method> getters = ReflectUtil.getAllGetters(obj.getClass());

		for (String fieldName : getters.keySet()) {
			Object value = ReflectUtil
					.invokeMethod(getters.get(fieldName), obj);
			if (null != value)
				map.put(fieldName, valuePerHandle.apply(value));
		}

		return map;

	}
	public static Object readFromTreeMap(Map treeMapData, String... path) {
		if(null== treeMapData){
			return null;
		}

		for(String p:path){
			Object subMap = treeMapData.get(p);
			if(null!=subMap && !(subMap instanceof Map) && path.length>1)
				throw new NestedException(ServiceCode.FAIL, "访问Map树的path错误");

			if(null!=subMap && subMap instanceof Map && path.length>1)
				return readFromTreeMap((Map) subMap, (String[]) Arrays.copyOfRange(path, 1, path.length));
			else
				return subMap;
		}
		return null;
	}


	public static Map<String, String> toStringMap(Object obj) {
		Assert.notNull(obj, ServiceCode.ERROR, "需要转化的对象不可以为空");

		Map<String, String> map = new HashMap<>();

		Map<String, Method> getters = ReflectUtil.getAllGetters(obj.getClass());

		for (String fieldName : getters.keySet()) {
			Object value = ReflectUtil
					.invokeMethod(getters.get(fieldName), obj);
			if (null != value)
				map.put(fieldName, String.valueOf(value));
		}

		return map;

	}

	public static <K, T> Map<K, T> toMap(Object... keyAndValue) {
		if (keyAndValue != null && keyAndValue.length > 0) {
			Map map;
			boolean firstIsMap = keyAndValue[0] instanceof Map;
			map = firstIsMap ? (Map) keyAndValue[0] : new HashMap();
			Assert.isTrue((keyAndValue.length - (firstIsMap ? 1 : 0)) % 2 == 0, ServiceCode.FAIL, "key-value必须成对出现");
			for (int i = 0, c = (keyAndValue.length - (firstIsMap ? 1 : 0)); i < c; i++, i++) {
				map.put(keyAndValue[i + (firstIsMap ? 1 : 0)], keyAndValue[i + (firstIsMap ? 1 : 0) + 1]);
			}
			return map;
		}
		return null;
	}

	/*
	 * 将根据参数 {@link Class} clazz 将Map转化为其一个实例。将根据class的属性从map的key中取值并赋值。
	 * Class必须有默认的无参构造函数。
	 * 从map中赋值时，null值，参数类型无法case或者无法转化的值将被忽略。
	 * 使用{@link ReflectUtil#invokeSetterWithDefaultTypeCoventer(Object, Method, Object, ReflectFeatureStatus)}进行set赋值。
	 */
	public static <T> T toObject(Map<String, Object> map, Class<T> clazz) {
		Assert.notNull(map, ServiceCode.ERROR, "需要生产对象的Map不可以为null");


		Map<String, Method> setters = ReflectUtil.getAllSetters(clazz);

		T dest = ReflectUtil.invokeNewInstance(clazz);

		ReflectFeatureStatus status = new ReflectFeatureStatus(null);
		for (String fieldName : setters.keySet()) {
			Object value = map.get(fieldName);
			if (null != value) {
				Method setter = setters.get(fieldName);
				ReflectUtil.invokeSetterWithDefaultTypeCoventer(dest, setter, value, status);
			}
		}

		return dest;

	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private static void _flat2Object(Object t, String nextPath, Object value, Object instanceFactory, Method instanceMethod) throws InvocationTargetException, IllegalAccessException {
		Class<?> clazz = t.getClass();
		int ci = -1, li = -1, li_r = -1;
		ci = nextPath.indexOf('.');
		if (((li = nextPath.indexOf("[")) > ci || li == -1) && ci > 0) {//L1属性此时是个对象
			String fieldName = nextPath.substring(0, ci);
			if (null == ReflectUtil.getGetter(clazz, fieldName))
				return;
			Object _v = ReflectUtil.invokeGetter(fieldName, t);

			if (null == _v) {
				Method setter = ReflectUtil.getSetter(clazz, fieldName);
				Class<?> pt = setter.getParameterTypes()[0];
				_v = instanceMethod.invoke(instanceFactory, pt);
				ReflectUtil.invokeSetter(fieldName, t, _v);
			}
			_flat2Object(_v, nextPath.substring(ci + 1), value, instanceFactory, instanceMethod);
		} else if (li > 0) {//L2属性此时是个数组
			String fieldName = nextPath.substring(0, li);
			if (null == ReflectUtil.getGetter(clazz, fieldName))
				return;
			Object _v = ReflectUtil.invokeGetter(fieldName, t);

			Method setter = ReflectUtil.getSetter(clazz, fieldName);
			Class<?> pt = setter.getParameterTypes()[0];
			if (null == _v) {
				if (null == _v && List.class.isAssignableFrom(pt)) { // 仅支持list类型
					if (pt.isInterface()) {
						_v = new ArrayList();
					} else if (pt.isAnonymousClass()) {
						_v = instanceMethod.invoke(instanceFactory, pt);
					}
					ReflectUtil.invokeSetter(fieldName, t, _v);
				}
			}
			List _l = (List) _v;
			String arrayIndex = nextPath.substring(li + 1, (li_r = nextPath.indexOf("]")));
			int n = nullOrBlank(arrayIndex) ? -1 : Integer.parseInt(arrayIndex); //LHY 2015-1-16 新增对没有下表[] 的支持

			//右中括号后面只会出现三种情况，1)结束，2).引用，3)[新数组引用
			if (li == nextPath.length()) {
				//nextPath结束
				_l.set(n, value);
				return;
			}
			String l_nextFlag = nextPath.substring(li_r + 1, li_r + 2); // . or [
			if (".".equals(l_nextFlag)) {
				Object _l_v;
				if (n > _l.size() || 0 == _l.size() || n < 0) {//没有值 //当中括号为[]时n<0
					for (int i = _l.size(); i <= n; i++) {
						_l.add(null);
					}
					Method getter = ReflectUtil.getGetter(clazz.getName().contains("$$EnhancerByCGLIB$$") ? clazz.getSuperclass() : clazz, fieldName);
					Class<?> _fc = ReflectUtil.getListActualType(getter.getGenericReturnType());
					_l_v = instanceMethod.invoke(instanceFactory, _fc);
					if (n == -1) _l.add(_l_v);
					else _l.set(n, _l_v); //当中括号为[]时直接添加
				} else {
					_l_v = _l.get(n);
				}
				_flat2Object(_l_v, nextPath.substring(li_r + (n > 0 ? 2 : 1)), value, instanceFactory, instanceMethod);
			} else if ("[".equals(l_nextFlag)) { // 数组嵌套
				throw new FunctionException(ServiceCode.FAIL, "暂不支持数组嵌套！");
			} else {
				throw new FunctionException(ServiceCode.FAIL, "非法字符：" + nextPath);
			}
		} else {//基本类型 需要case
			Method setter = ReflectUtil.getSetter(clazz, nextPath);
			if (null == setter || null == value) return;


			Class fieldClass = setter.getParameterTypes()[0];
			if (ReflectUtil.isSimpleType(fieldClass)) {
				ReflectUtil.invokeMethod(setter, t, ReflectUtil.caseObject(fieldClass, value, instanceFactory, instanceMethod, fieldClass));
			} else {

				value = value instanceof String ? value : value.getClass().isArray() && ((Object[]) value).length > 0 ? ((Object[]) value)[0].toString() : null;
				if (!nullOrBlank(value)) {//json 字符串
					if (((String) value).startsWith("{")) {
						Object o = JsonHelper.toJavaObject(value.toString(), fieldClass);
						ReflectUtil.invokeMethod(setter, t, o);
					} else if (((String) value).startsWith("[")) {
						Object o = JsonHelper.toJavaObject(value.toString(), setter.getGenericParameterTypes()[0]);
						ReflectUtil.invokeMethod(setter, t, o);
					}
				}
			}
		}
	}
//	public static void flat2MapTree(Map map, String path, Object value) {
////		//noinspection unchecked
////		flat2MapTree(map, path, value
////				, Map::put
////				, Map::get
////				, (root, fieldName) -> new HashMap<>()
////				, (root, fieldName) -> new ArrayList<>()
////				, List::add
////				, (root, integer) -> {
////					if(integer>0)
////						return root.get(integer);
////					else{
////						//noinspection unchecked
////						return ((List<Map>)root).stream()
////								.filter(map1 -> rowId.equals(map1.get(SYS_id)))
////								.findFirst().orElseGet(HashMap::new);
////					}
////				}
////
////		);
////		flat2MapTree(map, path, value
////				, (root, fieldName, bean) -> root.put(fieldName, bean)
////				, (root, fieldName) -> root.get(fieldName)
////				, (root, fieldName) -> new HashMap<>()
////				, (root, fieldName) -> new ArrayList<>()
////				, (root, integer, obj) -> root.add(integer, obj)
////				, (root, integer) -> root.get(integer)
////
////		);
//	}




	//LHY 2016/12/31 晚整理的链式扁平化数据向Objec或Json对象或MapTree结构的数据的底层抽象方法；之前的_flat2Object 都可以通过此方法实现，暂时没有改造
	@SuppressWarnings({"rawtypes", "unchecked"})
	public static<T> void flat2MapTree(T root, String path, String contextPath, Object value
			, CommSetter<T, String, Object> setter // 赋值
			, CommGetter<T, String, Object> getter //取值
			, CommConstructor<T, String, Object> beanConstructor //产生对象
			, CommConstructor<T, String, List> arrayConstructor //产生数组

			, CommSetter<List, String, Object> arrayItemSetter // 赋值
			, CommGetter<List, String, Object> arrayItemGetter //取值
	){

		int ci = -1, li = -1, li_r = -1;
		ci = path.indexOf('.');
		if (ci > 0 && ((li = path.indexOf("[")) > ci || li == -1)) {//L1属性此时是个对象
			String fieldName = path.substring(0, ci);
			Object obj = getter.visit(root, fieldName);
			if (null == obj) {
				obj = beanConstructor.create(root, fieldName);
				setter.assign(root, fieldName, obj);
			}
			flat2MapTree((T) obj, path.substring(ci + 1), (nullOrBlank(contextPath)?"":contextPath+".")+path.substring(0, ci), value, setter, getter, beanConstructor, arrayConstructor, arrayItemSetter, arrayItemGetter);
		} else if (li > 0) {//L2属性此时是个数组
			String fieldName = path.substring(0, li);
			String arrayIndexStr = path.substring(li + 1, (li_r = path.indexOf("]")));
			int arrayIndex = nullOrBlank(arrayIndexStr) ? -1 : Integer.parseInt(arrayIndexStr); //LHY 2015-1-16 新增对没有下表[] 的支持

			List arr = (List) getter.visit(root, fieldName);
			if (null == arr) {
				arr = arrayConstructor.create(root, fieldName);
				setter.assign(root, fieldName, arr);
			}

			//右中括号后面只会出现三种情况，1)结束，2).引用，3)[新数组引用
			if (li == path.length()) {
				//nextPath结束
				arrayItemSetter.assign(arr, arrayIndexStr, value);
				return;
			}
			String l_nextFlag = path.substring(li_r + 1, li_r + 2); // . or [
			if (".".equals(l_nextFlag)) {
				Object arrItem = arrayItemGetter.visit(arr, (nullOrBlank(contextPath)?"":contextPath+".")+path.substring(0, (li_r = path.indexOf("]")+1)));
				if (null==arrItem && (arrayIndex > arr.size() || 0 == arr.size() || arrayIndex < 0)) {//没有值 //当中括号为[]时n<0
					for (int i = arr.size(); i <= arrayIndex; i++) {
						arr.add(null);
					}
					arrItem = beanConstructor.create(root, String.valueOf(arrayIndex));
					if (arrayIndex == -1)
						arr.add(arrItem);
					else
						arr.set(arrayIndex, arrItem); //当中括号为[]时直接添加
				}
				flat2MapTree((T)arrItem, path.substring(li_r + (arrayIndex > 0 ? 2 : 1)),(nullOrBlank(contextPath)?"":contextPath+".")+path.substring(0,li_r), value, setter, getter, beanConstructor, arrayConstructor, arrayItemSetter, arrayItemGetter);
			} else if ("[".equals(l_nextFlag)) { // 数组嵌套
				throw new FunctionException(ServiceCode.FAIL, "暂不支持数组嵌套！");
			} else {
				throw new FunctionException(ServiceCode.FAIL, "非法字符：" + path);
			}
		} else {//基本类型 需要case
			setter.assign(root, path, value);
		}
	}
	private static Object _toFlatData(Object o, String root, Map<String, String> rootMap) {
		Assert.notNull(o, ServiceCode.ERROR, "需要扁平化的对象不能为空！");

		Class<?> clazz = o.getClass();
		Map<String, String> map = null == rootMap ? new LinkedHashMap<String, String>() : rootMap;

		if (Collection.class.isAssignableFrom(clazz)) {
			Collection<?> _c = (Collection<?>) o;
			int i = 0;
			for (Object value : _c) {
				_toFlatData(value, new StringBuilder(root).append("[").append(i++).append("]").toString(), map);
			}
		} else if (Map.class.isAssignableFrom(clazz)) {
			Map<?, ?> _m = ((Map<?, ?>) o);
			for (Object key : _m.keySet()) {
				_toFlatData(_m.get(key), new StringBuilder(root).append(nullOrBlank(root) ? "" : ".").append(key).toString(), map);
			}
		} else if (ReflectUtil.isSimpleType(clazz) || Enum.class.isAssignableFrom(clazz)) { // 属性是简单类型,非Map及Collection类型
			map.put(new StringBuilder(root).toString(), null == o ? null : o.toString());
		} else {
			Map<String, Method> getters = ReflectUtil.getAllUserGetters(o.getClass());
			for (String fieldName : getters.keySet()) {
				Method getter = getters.get(fieldName);
				Object value = ReflectUtil.invokeMethod(getter, o);
				if (null == value) continue;
				_toFlatData(value, new StringBuilder(root).append(nullOrBlank(root) ? "" : ".").append(fieldName).toString(), map);
			}

		}

		return map;
	}

	private static Object _toMapTree(Object o) {

		Assert.notNull(o, ServiceCode.ERROR, "需要MapTree化的对象不能为空！");
		Class<?> clazz = o.getClass();
		if (ReflectUtil.isSimpleType(clazz) || Enum.class.isAssignableFrom(clazz)) {
			return o.toString();
		}

		Map<String, Object> map = new LinkedHashMap<String, Object>();
		if (Collection.class.isAssignableFrom(clazz)) {
			Collection<?> _c = (Collection<?>) o;
			int i = 0;
			for (Object value : _c) {
				map.put(String.valueOf(i++), _toMapTree(value));
			}
		} else if (Map.class.isAssignableFrom(clazz)) {
			Map<?, ?> _m = ((Map<?, ?>) o);
			for (Object key : _m.keySet()) {
				map.put(key == null ? null : key.toString(), _m.get(key));
			}
		} else {
			Map<String, Method> getters = ReflectUtil.getAllUserGetters(o.getClass());
			for (String fieldName : getters.keySet()) {
				Method getter = getters.get(fieldName);
				Object value = ReflectUtil.invokeMethod(getter, o);
				if (null == value) continue;
				map.put(fieldName, _toMapTree(value));
			}
		}

		return map;
	}
}
